package org.jeecg.activiti.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.ExclusiveGateway;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.ParallelGateway;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.StartEvent;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.jeecg.activiti.entity.ActReModelExtend;
import org.jeecg.activiti.entity.ActReModelFormData;
import org.jeecg.activiti.entity.ActReModelFormDeployment;
import org.jeecg.activiti.entity.ActivitiConstant;
import org.jeecg.activiti.entity.ProcessNodeVo;
import org.jeecg.activiti.service.IActReModelExtendService;
import org.jeecg.activiti.service.IActReModelFormDataService;
import org.jeecg.activiti.service.IActReModelFormDeploymentService;
import org.jeecg.activiti.service.IActivitiInstanceService;
import org.jeecg.activiti.service.IActivitiTaskService;
import org.jeecg.activiti.util.DefaultProcessDiagramGenerator;
import org.jeecg.common.api.dto.message.BusMessageDTO;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.online.cgform.entity.OnlCgformHead;
import org.jeecg.modules.online.cgform.service.IOnlCgformFieldService;
import org.jeecg.modules.online.cgform.service.IOnlCgformHeadService;
import org.jeecg.modules.online.cgform.util.SqlSymbolUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.googlecode.aviator.AviatorEvaluator;

import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.log.Log;

/**
 * 流程实例service实现类
 *
 * @author dousw
 * @version 1.0
 * @date 2020/9/16 14:08
 */

@Slf4j
@Service
public class ActivitiInstanceServiceImpl implements IActivitiInstanceService {
	
	@Value("${jeecg.wx.bpmn.agentId}")
	private Integer agentId;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private IActReModelFormDeploymentService iActReModelFormDeploymentService;

    @Autowired
    private IActivitiTaskService iActivitiTaskService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private ISysBaseAPI iSysBaseAPI;
    
    @Autowired
    private IActReModelExtendService actReModelExtendService;
    
    @Autowired
    private IOnlCgformHeadService onlCgformHeadService;
    
    @Autowired
    private IOnlCgformFieldService fieldService;
    
    @Autowired
    private IActReModelFormDataService iActReModelFormDataService;

    @Override
    public ProcessNodeVo getFirstNode(String processDefinitionId) {
        ProcessNodeVo node = new ProcessNodeVo();

        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        List<Process> processes = bpmnModel.getProcesses();
        Collection<FlowElement> elements = processes.get(0).getFlowElements();
        // 流程开始节点
        StartEvent startEvent = null;
        for (FlowElement element : elements) {
            if (element instanceof StartEvent) {
                startEvent = (StartEvent) element;
                break;
            }
        }
        FlowElement e = null;
        // 判断开始后的流向节点
        SequenceFlow sequenceFlow = startEvent.getOutgoingFlows().get(0);
        for (FlowElement element : elements) {
            if(element.getId().equals(sequenceFlow.getTargetRef())){
                if(element instanceof UserTask){
                    e = element;
                    node.setType(ActivitiConstant.NODE_TYPE_TASK);
                    break;
                }else if(element instanceof ExclusiveGateway){
                    e = element;
                    node.setType(ActivitiConstant.NODE_TYPE_EG);
                    break;
                }else if(element instanceof ParallelGateway){
                    e = element;
                    node.setType(ActivitiConstant.NODE_TYPE_PG);
                    break;
                }else{
                    throw new RuntimeException("流程设计错误，开始节点后只能是用户任务节点、排他网关、并行网关");
                }
            }
        }
        // 排他、平行网关直接返回
        if(e instanceof ExclusiveGateway || e instanceof ParallelGateway){
            return node;
        }
        node.setTitle(e.getName());
        return node;
    }

    @Autowired
    private ProcessEngine processEngine;

    @Override
    public String getFlowImgByInstanceId(String processInstanceId) {
        InputStream imageStream = null;

        try {
            if (StringUtils.isEmpty(processInstanceId)) {
                return null;
            }
            // 获取历史流程实例
            HistoricProcessInstance historicProcessInstance = historyService
                    .createHistoricProcessInstanceQuery()
                    .processInstanceId(processInstanceId).singleResult();
            // 获取流程中已经执行的节点，按照执行先后顺序排序
            List<HistoricActivityInstance> historicActivityInstances = historyService
                    .createHistoricActivityInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .orderByHistoricActivityInstanceId()
                    .asc().list();
            // 高亮已经执行流程节点ID集合
            List<String> highLightedActivitiIds = new ArrayList<>();
            for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                // 用默认颜色
                highLightedActivitiIds.add(historicActivityInstance.getActivityId());
            }

            List<String> currIds = historicActivityInstances.stream()
                    .filter(item -> StringUtils.isEmpty(item.getEndTime()))
                    .map(HistoricActivityInstance::getActivityId).collect(Collectors.toList());

            // 获得流程引擎配置
            ProcessEngineConfiguration processEngineConfiguration = processEngine.getProcessEngineConfiguration();


            BpmnModel bpmnModel = repositoryService
                    .getBpmnModel(historicProcessInstance.getProcessDefinitionId());
            // 高亮流程已发生流转的线id集合
            List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, historicActivityInstances);

            imageStream = new DefaultProcessDiagramGenerator().generateDiagram(
                    bpmnModel,
                    "png",
                    highLightedActivitiIds,//所有活动过的节点，包括当前在激活状态下的节点
                    currIds,//当前为激活状态下的节点
                    highLightedFlowIds,//活动过的线
                    "宋体",
                    "宋体",
                    "宋体",
                    processEngineConfiguration.getClassLoader(),
                    1.0);
            // 将图片文件转化为字节数组字符串，并对其进行Base64编码处理
            byte[] data = new byte[imageStream.available()];
            imageStream.read(data);
            return Base64.getEncoder().encodeToString(data);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (imageStream != null) {
                try {
                    imageStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return null;
    }

     /**
      *  获取已经流转的线
      *  @param bpmnModel
      * @param historicActivityInstances
      * @return
      */
    private static List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
        // 高亮流程已发生流转的线id集合
        List<String> highLightedFlowIds = new ArrayList<>();
        // 全部活动节点
        List<FlowNode> historicActivityNodes = new ArrayList<>();
        // 已完成的历史活动节点
        List<HistoricActivityInstance> finishedActivityInstances = new ArrayList<>();

        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
            FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);
            historicActivityNodes.add(flowNode);
            if (historicActivityInstance.getEndTime() != null) {
                finishedActivityInstances.add(historicActivityInstance);
            }
        }

        FlowNode currentFlowNode = null;
        FlowNode targetFlowNode = null;
        // 遍历已完成的活动实例，从每个实例的outgoingFlows中找到已执行的
        for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) {
            // 获得当前活动对应的节点信息及outgoingFlows信息
            currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
            List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();

            /**
             * 遍历outgoingFlows并找到已已流转的 满足如下条件认为已已流转：
             * 1.当前节点是并行网关或兼容网关，则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
             * 2.当前节点是以上两种类型之外的，通过outgoingFlows查找到的时间最早的流转节点视为有效流转
             */
            if ("parallelGateway".equals(currentActivityInstance.getActivityType())
                    || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
                // 遍历历史活动节点，找到匹配流程目标节点的
                for (SequenceFlow sequenceFlow : sequenceFlows) {
                    targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
                    if (historicActivityNodes.contains(targetFlowNode)) {
                        highLightedFlowIds.add(sequenceFlow.getId());
                    }
                }
            } else {
                List<Map<String, Object>> tempMapList = new ArrayList<>();
                for (SequenceFlow sequenceFlow : sequenceFlows) {
                    for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                        if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
                            Map<String, Object> map = new HashMap<>();
                            map.put("highLightedFlowId", sequenceFlow.getId());
                            map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());
                            tempMapList.add(map);
                        }
                    }
                }

                if (!CollectionUtils.isEmpty(tempMapList)) {
                    // 遍历匹配的集合，取得开始时间最早的一个
                    long earliestStamp = 0L;
                    String highLightedFlowId = null;
                    for (Map<String, Object> map : tempMapList) {
                        long highLightedFlowStartTime = Long.valueOf(map.get("highLightedFlowStartTime").toString());
                        if (earliestStamp == 0 || earliestStamp == highLightedFlowStartTime) {
                            highLightedFlowId = map.get("highLightedFlowId").toString();
                            earliestStamp = highLightedFlowStartTime;
                        }
                    }

                    highLightedFlowIds.add(highLightedFlowId);
                }

            }

        }
        return highLightedFlowIds;
    }

    @Override
    public String getFlowXmlByInstanceId(String processInstanceId) {
        if (StringUtils.isEmpty(processInstanceId)) {
            return null;
        }

        // 查询流程xml
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstanceId);

        BpmnXMLConverter converter = new BpmnXMLConverter();
        //把bpmnModel对象转换成字符
        byte[] bytes = converter.convertToXML(bpmnModel);
        String xmlContenxt = bytes.toString();
        Log.debug(xmlContenxt);
        return null;
    }

    @Override
    public void handleProcessDuplicate(ActReModelFormData actReModelFormData, Map<String, Object> variables, String procInstId, LoginUser sysUser) {
        List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(procInstId).list();
        Task task = taskService.createTaskQuery().executionId(executions.get(1).getId()).singleResult();

        ActReModelFormDeployment actReModelFormDeployment = iActReModelFormDeploymentService.getById(actReModelFormData.getFormId());
        String htmlJson = actReModelFormDeployment.getHtmlJson();
        JSONObject jsonObject = JSONObject.parseObject(htmlJson);
        JSONArray jsonArray = jsonObject.getJSONObject("config").getJSONArray("cclist");
        if (jsonArray != null) {
            Iterator<Object> iterator = jsonArray.iterator();
            while (iterator.hasNext()) {
                String formKey = iterator.next().toString();
                if (variables.get(formKey) != null) {
                    String usernames = variables.get(formKey).toString();
                    iActivitiTaskService.handelDuplicate(usernames, procInstId, actReModelFormData.getModelId(), task, sysUser);
                }
            }
        }
    }

    @Override
    public void handleBacklogMsg(ProcessInstance pi, LoginUser sysUser) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(pi.getProcessDefinitionId()).singleResult();
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(pi.getId()).list();
        ActReModelFormData actReModelFormData = iActReModelFormDataService.getOne(new QueryWrapper<ActReModelFormData>().eq("process_instance_id", pi.getId()));
        tasks.forEach(task -> {
            String toUser = task.getAssignee();
            if (!StringUtils.isEmpty(toUser)) {
            	String title = "【审核】"+processDefinition.getName();
                String content = getInstanceName(actReModelFormData);
                // iSysBaseAPI.sendSysMsg(sysUser.getUsername(),toUser, content, content, task.getId(), "url", "/task/MyTaskList");
                
                BusMessageDTO msg = new BusMessageDTO();
                msg.setFromUser(sysUser.getUsername());
                msg.setToUser(toUser);
                msg.setTitle(title);
                msg.setContent(content);
                msg.setCategory("2");
                msg.setBusType("bpm");
                msg.setBusId(task.getId());
                msg.setThirdUrl("/processHandle?id="+task.getId());
                iSysBaseAPI.sendBusAnnouncement(msg);
                /*try{
                	WxCpService wxCpService =  WxCpConfiguration.getCpService(agentId);
            		WxCpMessageService wxCpMessageService = new WxCpMessageServiceImpl(wxCpService);
            		String url = wxCpService.getOauth2Service().buildAuthorizationUrl(wxCpService.getWxCpConfigStorage().getDomain()+"/processHandle?id="+task.getId(),null);
            		LoginUser user = iSysBaseAPI.getUserByName(toUser);
            		WxCpMessage message = WxCpMessage.TEXTCARD().toUser(user.getThirdId()).agentId(wxCpService.getWxCpConfigStorage().getAgentId()).title(title).description(content).url(url).build();
            		wxCpMessageService.send(message);
                }catch (Exception e) {
				}*/
            }
        });
    }
    
 // 获取业务标题
    private String getInstanceName(ActReModelFormData actReModelFormData){
    	if(actReModelFormData == null){
    		return "无业务标题";
    	}
    	ActReModelExtend modelExtend = actReModelExtendService.getOne(new QueryWrapper<ActReModelExtend>().eq("model_id", actReModelFormData.getModelId()));
    	Map<String, Object> variables;
        if(actReModelFormData.getTableId() == null){
    		variables = JSONObject.toJavaObject(JSONObject.parseObject(actReModelFormData.getFormData()), Map.class);
        }else{
        	OnlCgformHead head = onlCgformHeadService.getById(actReModelFormData.getTableId());
         	variables = this.fieldService.queryBpmData(actReModelFormData.getTableId(), head.getTableName(), actReModelFormData.getDataId());
         	variables = SqlSymbolUtil.getValueType(variables);
        }
        String expression = modelExtend.getTitle();
        if(StringUtils.isEmpty(expression)){
        	return "无业务标题";
        }
        String title;
        try{
        	title = AviatorEvaluator.execute(expression,variables)+"";
        }catch(Exception e) {
        	log.error("expression========>"+expression);
        	title = "标题加载失败";
        }
        return title;
    }
}
